package com.baoyouqun.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baoyouqun.entity.FileInfoLocal;
import com.baoyouqun.mapper.FileInfoLocalMapper;
import com.baoyouqun.service.IFileStorageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

/**
 * 文件存储服务实现类
 */
@Slf4j
@Service
public class FileStorageServiceImpl extends ServiceImpl<FileInfoLocalMapper, FileInfoLocal> implements IFileStorageService {

    /**
     * 日期格式化器，用于生成每日文件夹
     */
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd");
    @Value("${file.storage.base-path}")
    private String basePath;
    @Value("${file.storage.temp-path}")
    private String tempPath;
    @Value("${file.storage.url-prefix}")
    private String urlPrefix;

    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public FileInfoLocal upload(MultipartFile file, String bucket, Long uploaderId) throws IOException {
        // 用于记录可能的URL（异常发生时可能尚未生成）
        String currentUrl = null;
        try {
            // 1. 参数校验
            if (file.isEmpty()) {
                throw new IllegalArgumentException("上传文件不能为空");
            }

            // 新增：检查文件大小（单位：字节）
            long maxSize = 10 * 1024 * 1024; // 10MB，根据业务需求设置
            if (file.getSize() > maxSize) {
                throw new IllegalArgumentException("文件大小超过限制，最大支持" + maxSize / (1024 * 1024) + "MB");
            }

            // 2. 准备存储路径：basePath/bucket/yyyy/MM/dd
            String datePath = LocalDate.now().format(DATE_FORMATTER);
            String relativePath = bucket + "/" + datePath;
            Path storageDirPath = Paths.get(basePath, relativePath);

            // 创建存储目录（使用Files更可靠）
            if (!Files.exists(storageDirPath)) {
                Files.createDirectories(storageDirPath);
            }

            // 3. 生成存储文件名（修复扩展名处理）
            String originalFileName = file.getOriginalFilename();
            String fileExt = "";
            if (originalFileName != null && originalFileName.contains(".")) {
                fileExt = originalFileName.substring(originalFileName.lastIndexOf("."));
            }
            String storageFileName = UUID.randomUUID() + fileExt;

            // 4. 计算文件MD5（用于去重）
            String md5 = calculateFileMd5(file);

            // 5. 保存文件到本地
            Path destFilePath = storageDirPath.resolve(storageFileName);
            file.transferTo(destFilePath);

            // 6. 保存文件信息到数据库
            FileInfoLocal fileInfo = new FileInfoLocal();
            fileInfo.setOriginalFileName(originalFileName);
            fileInfo.setStorageFileName(storageFileName);
            fileInfo.setRelativePath(relativePath);
            fileInfo.setFileType(file.getContentType());
            fileInfo.setFileSize(file.getSize());
            fileInfo.setMd5(md5);
            fileInfo.setBucket(bucket);
            fileInfo.setUploaderId(uploaderId);

            String fileUrl = urlPrefix + bucket + "/" + datePath + "/" + storageFileName;
            fileUrl = fileUrl.replace(File.separator, "/");
            fileUrl = fileUrl.replace("files//", "files/");
            fileInfo.setFileUrl(fileUrl);
            // 更新当前URL变量（用于日志）
            currentUrl = fileUrl;

            baseMapper.insert(fileInfo);

            log.info("文件上传成功: {}", originalFileName);
            return fileInfo;
        } catch (IllegalArgumentException e) {
            // 捕获参数异常（包括文件为空、大小超限等），直接返回错误提示
            log.warn("文件上传参数错误: {}", e.getMessage());
            throw e; // 假设全局异常处理器会将此异常转换为友好响应
        } catch (RuntimeException e) {
            // 记录其他运行时异常日志
            log.error("捕获运行时异常，URL:{}，异常类型:{}，消息:{}",
                    currentUrl,
                    e.getClass().getName(),
                    e.getMessage());
            throw e;
        }
    }


    @Override
    public InputStream download(Long fileId) throws IOException {
        // 1. 查询文件信息
        FileInfoLocal fileInfo = getById(fileId);
        if (fileInfo == null) {
            throw new FileNotFoundException("文件不存在，ID: " + fileId);
        }

        // 2. 构建文件路径
        Path filePath = Paths.get(basePath, fileInfo.getRelativePath(), fileInfo.getStorageFileName());
        if (!Files.exists(filePath)) {
            throw new FileNotFoundException("文件不存在于本地存储: " + filePath);
        }

        // 3. 返回文件输入流
        return Files.newInputStream(filePath);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delete(Long fileId) {
        // 1. 查询文件信息
        FileInfoLocal fileInfo = getById(fileId);
        if (fileInfo == null) {
            log.warn("文件不存在，无法删除，ID: {}", fileId);
            return false;
        }

        // 2. 删除本地文件
        Path filePath = Paths.get(basePath, fileInfo.getRelativePath(), fileInfo.getStorageFileName());
        try {
            if (Files.exists(filePath)) {
                Files.delete(filePath);
            }
        } catch (IOException e) {
            log.error("本地文件删除失败: {}", filePath, e);
            // 本地删除失败时回滚事务
            throw new RuntimeException("文件删除失败", e);
        }

        // 3. 删除数据库记录
        return removeById(fileId);
    }

    @Override
    public FileInfoLocal getFileInfoLocal(Long fileId) {
        FileInfoLocal fileInfo = getById(fileId);
        if (fileInfo != null) {
            // 计算并设置文件访问URL
//            String fileUrl = urlPrefix + fileInfo.getRelativePath() + "/" + fileInfo.getStorageFileName();
            String fileUrl = urlPrefix + Paths.get(fileInfo.getRelativePath(), fileInfo.getStorageFileName()).toString().replace(File.separator, "/");

            fileInfo.setFileUrl(fileUrl);
        }
        return fileInfo;
    }

    @Override
    public IPage<FileInfoLocal> queryFiles(int pageNum, int pageSize, String bucket, String fileName, String fileType) {
        Page<FileInfoLocal> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<FileInfoLocal> wrapper = new LambdaQueryWrapper<>();
        wrapper
                .eq(FileInfoLocal::getBucket, bucket)
                .eq(FileInfoLocal::getStorageFileName, fileName)
                .eq(FileInfoLocal::getFileType, fileType);
        page = page(page, wrapper);
        Page<FileInfoLocal> resultPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());

        // 为每个文件设置访问URL
        resultPage.getRecords().forEach(fileInfo -> {
//            String fileUrl = urlPrefix + fileInfo.getRelativePath() + "/" + fileInfo.getStorageFileName();
            String fileUrl = urlPrefix + Paths.get(fileInfo.getRelativePath(), fileInfo.getStorageFileName()).toString().replace(File.separator, "/");

            fileInfo.setFileUrl(fileUrl);
        });
        return resultPage;

    }

//    @Override
//    public String calculateFileMd5(MultipartFile file) throws IOException {
//        // 确保临时目录存在
//        Path tempDirPath = Paths.get(tempPath);
//        if (!Files.exists(tempDirPath)) {
//            Files.createDirectories(tempDirPath);
//        }
//
//        // 使用try-with-resources自动关闭临时文件
//        Path tempFilePath = Files.createTempFile(tempDirPath, "temp", null);
//        try {
//            file.transferTo(tempFilePath);
//            try (InputStream is = Files.newInputStream(tempFilePath)) {
//                return DigestUtils.md5DigestAsHex(is);
//            }
//        } finally {
//            // 确保临时文件被删除
//            Files.deleteIfExists(tempFilePath);
//        }
//    }

    @Override
    public String calculateFileMd5(MultipartFile file) throws IOException {
        try (InputStream is = file.getInputStream()) {
            return DigestUtils.md5DigestAsHex(is);
        }
    }

}